-
-
Notifications
You must be signed in to change notification settings - Fork 638
Add Shakapacker precompile hook with ReScript support to Pro dummy app #1977
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Warning Rate limit exceeded@justin808 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 35 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
WalkthroughAdds a shared Shakapacker precompile hook and wiring: dummy and generator hook scripts delegate to a gem-relative shared Ruby hook that detects/builds ReScript and conditionally generates React on Rails packs based on Rails configuration. Changes
Sequence Diagram(s)sequenceDiagram
participant Invoker as Shakapacker
participant Dummy as Dummy Hook
participant Shared as Shared Hook
participant Rails as Rails App
participant PM as Package Manager
participant PackGen as Pack Generation
Invoker->>Dummy: execute precompile hook
Dummy->>Dummy: resolve gem_root & shared_hook path
alt shared hook exists
Dummy->>Shared: load(shared_hook)
Shared->>Shared: discover Rails root
opt ReScript present and script defined
Shared->>PM: run build:rescript (yarn/npm)
PM-->>Shared: build result
end
opt auto-load bundles/components
Shared->>PackGen: invoke pack generation (rails task / bundle exec)
PackGen-->>Rails: generate packs
end
Shared-->>Invoker: exit success
else shared hook missing
Dummy-->>Invoker: warn + exit 1
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Code Review: Fix Pro dummy app Playwright test timeouts✅ Overall AssessmentThis is a well-structured fix that correctly addresses the Playwright test timeout issue by aligning the Pro dummy app with the open-source configuration from PR #1972. The changes are consistent, well-documented, and follow established patterns. 📋 Summary of Changes
✅ Strengths1. Root Cause Analysis
2. Code Quality
3. Documentation
4. Consistency
🔍 Detailed Reviewapplication.html.erb (lines 24-31)✅ Good: Updated comment accurately describes the defer strategy purpose shakapacker.yml (lines 22-25)✅ Excellent: Includes security warning about precompile hook bin/shakapacker-precompile-hook (entire file)✅ Excellent: Robust implementation with multiple safety checks Specific highlights:
🐛 Potential IssuesNone identified - The code is production-ready. 🔒 Security Review✅ Command injection prevention: Uses array form for all ⚡ Performance Considerations✅ Minimal overhead: Early returns prevent unnecessary work 🧪 Test CoverageTest Plan Status: Recommendations for testing:
💡 Suggestions1. Test Plan Completion 2. Verification Script (optional) # In CI or locally
RAILS_ENV=test bin/shakapacker-precompile-hook
echo "Hook exit code: $?"3. Documentation (optional, future work) 📝 Checklist
🎯 RecommendationLGTM after CI passes ✅ This is a solid fix that:
Once the Playwright tests pass in CI, this is ready to merge. 📚 References
Review generated with assistance from Claude Code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook (1)
72-78: Surface skip reason when Bundler is unavailable. Right now wereturnsilently ifbundle_availableis false (or later when the task is missing), so folks won’t know packs were skipped. Consider emitting awarnbefore those early returns to make the hook’s behavior explicit during CI troubleshooting.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erb(1 hunks)react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook(1 hunks)react_on_rails_pro/spec/dummy/config/shakapacker.yml(1 hunks)
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Learnt from: theforestvn88
Repo: shakacode/react_on_rails PR: 1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-07-27T10:08:35.868Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Learnt from: theforestvn88
Repo: shakacode/react_on_rails PR: 1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-10-08T20:53:47.076Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-02-13T16:50:47.848Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1696
File: node_package/src/RSCPayloadContainer.ts:0-0
Timestamp: 2025-04-09T12:56:10.756Z
Learning: In the react_on_rails codebase, RSC payloads are already stringified using `JSON.stringify()` before being processed by the `escapeScript` function, which handles escaping of special characters. The function only needs to handle specific HTML markers like comments and closing script tags.
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erbreact_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hookreact_on_rails_pro/spec/dummy/config/shakapacker.yml
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erbreact_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erbreact_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook
📚 Learning: 2024-07-27T10:08:35.868Z
Learnt from: theforestvn88
Repo: shakacode/react_on_rails PR: 1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-07-27T10:08:35.868Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Applied to files:
react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erb
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erb
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
Applied to files:
react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erbreact_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook
📚 Learning: 2025-02-13T16:50:26.861Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/turbolinksUtils.ts:34-36
Timestamp: 2025-02-13T16:50:26.861Z
Learning: In React on Rails, when checking for Turbolinks version 5 using `turbolinksVersion5()`, always ensure `Turbolinks` exists first by checking `turbolinksInstalled()` to prevent TypeError when accessing properties.
Applied to files:
react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erb
📚 Learning: 2025-02-13T16:50:47.848Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-02-13T16:50:47.848Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.
Applied to files:
react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erb
📚 Learning: 2025-04-09T12:56:10.756Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1696
File: node_package/src/RSCPayloadContainer.ts:0-0
Timestamp: 2025-04-09T12:56:10.756Z
Learning: In the react_on_rails codebase, RSC payloads are already stringified using `JSON.stringify()` before being processed by the `escapeScript` function, which handles escaping of special characters. The function only needs to handle specific HTML markers like comments and closing script tags.
Applied to files:
react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erb
🔇 Additional comments (2)
react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erb (1)
27-33: Defer attribute restores hydration order. Switching todefermirrors the OSS dummy fix and guarantees component packs register before hydration—looks solid.react_on_rails_pro/spec/dummy/config/shakapacker.yml (1)
22-25: Hook config lines up with the automation. Wiring the precompile hook here cleanly connects the new script into pack generation flow.
Code Review: PR #1977OverviewThis PR successfully addresses Playwright E2E test timeouts in the Pro dummy app by aligning with the defer loading strategy fix from PR #1972. The changes are well-documented and the root cause analysis is thorough. ✅ Strengths1. Excellent Problem Analysis
2. Consistent ImplementationThe 3. Robust Hook ScriptThe precompile hook demonstrates several best practices:
4. Security AwarenessThe 🔍 Code Quality ObservationsPrecompile Hook Script (
|
Code Review - PR #1977SummaryThis PR successfully addresses Playwright E2E test timeouts in the Pro dummy app by aligning it with the defer loading strategy from PR #1972. The changes are well-motivated, properly documented, and follow established patterns from the open-source codebase. Positive Aspects1. Excellent Problem Analysis ✅The PR description clearly explains:
2. Code Consistency ✅
3. Security Awareness ✅The # SECURITY: Only reference trusted scripts within your project. Ensure the hook path
# points to a file within the project root that you control.4. Robust Hook Implementation ✅The precompile hook demonstrates good practices:
Observations & Minor Suggestions1. Comment Update in Layout
|
c8be1f5 to
36d768b
Compare
36d768b to
cbb7df2
Compare
Code Review: PR #1977OverviewThis PR fixes Playwright E2E test timeouts in the Pro dummy app by applying the same ✅ Strengths
🔍 Issues & Recommendations1. Security: Command Injection Risk (Medium Priority)Location: success = if yarn_available
system("yarn", "build:rescript")
elsif npm_available
system("npm", "run", "build:rescript")Issue: The script uses the array form of Recommendation: Add a comment explaining the security consideration: # Use array form to prevent shell injection
success = if yarn_available
system("yarn", "build:rescript")2. Error Handling: Silent Failures (Low Priority)Location: else
warn "⚠️ Warning: Neither yarn nor npm found. Skipping ReScript build."
returnIssue: When neither package manager is found, the script warns but continues. If ReScript is required, this could lead to broken builds that fail later. Recommendation: Consider making this configurable or failing fast if ReScript config exists: else
warn "❌ Error: ReScript config found but no package manager available"
exit 1
end3. Code Quality: Complex Regex (Low Priority)Location: has_auto_load = config_file =~ /^\s*(?\!#).*config\.auto_load_bundle\s*=/
has_components_subdir = config_file =~ /^\s*(?\!#).*config\.components_subdirectory\s*=/Issue: The negative lookahead Recommendation: Use a more robust regex: # Match lines that don't start with # (after optional whitespace)
has_auto_load = config_file =~ /^\s*[^#]*config\.auto_load_bundle\s*=/
has_components_subdir = config_file =~ /^\s*[^#]*config\.components_subdirectory\s*=/Or use Ruby parsing: has_auto_load = config_file.lines.any? { |line| line.strip[0] \!= '#' && line.include?('config.auto_load_bundle =') }4. Missing Test Coverage (Medium Priority)Issue: No tests for the precompile hook script. Given its critical role in the build process, it should have unit tests. Recommendation: Add tests in
5. Documentation: Shakapacker Version (Low Priority)Location: The comment references Shakapacker docs, but doesn't specify the minimum version required for Recommendation: Add version requirement to the comment: # Hook to run before webpack compilation (requires Shakapacker >= X.X.X)🧪 Testing ConsiderationsThe PR description mentions a test plan but doesn't show completed checkboxes. Before merging:
🎯 Performance ConsiderationsPositive: Using Neutral: The precompile hook adds build time overhead, but this is necessary for auto-generated packs. 📋 SummaryApprove with minor suggestions. The core fix is sound and well-implemented. The issues identified are mostly improvements rather than blockers:
The PR successfully addresses the test timeout issue and aligns the Pro dummy app with the open-source configuration. Great work documenting the root cause and solution! 📚 References
|
Code Review for PR #1977Thank you for this PR! The changes effectively address the Playwright test timeout issue by aligning the Pro dummy app with the open-source configuration. Here's my detailed review: ✅ Strengths1. Root Cause Analysis is SolidThe PR correctly identifies the race condition caused by 2. Consistency Across CodebaseAligning the Pro dummy app with the open-source dummy app (from #1972) ensures consistent behavior and reduces maintenance burden. 3. Comprehensive SolutionThe PR includes all necessary pieces:
4. Good DocumentationThe inline comments explain why 🔍 Observations & Suggestions1. Code Duplication: Precompile HookThe Pro version (line 75): task_list = IO.popen(["bundle", "exec", "rails", "-T"], err: %i[child out], &:read)Open-source version (line 76): task_list = IO.popen(["bundle", "exec", "rails", "-T"], err: [:child, :out], &:read)The Pro version uses Suggestion: Consider one of these approaches to reduce duplication:
2. RuboCop Violation in Pro VersionThe open-source version has a RuboCop disable comment for Action Required: # Add these lines to react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook
# rubocop:disable Metrics/CyclomaticComplexity
def generate_packs_if_needed
# ... existing code ...
end
# rubocop:enable Metrics/CyclomaticComplexity3. Security Note is Well-PlacedThe security comment in 4. Test CoverageThe PR description mentions test timeouts were the issue, and the fix ensures proper hydration. However:
5. Comment Accuracy in
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erb(1 hunks)react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook(1 hunks)react_on_rails_pro/spec/dummy/config/initializers/react_on_rails.rb(1 hunks)react_on_rails_pro/spec/dummy/config/shakapacker.yml(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- react_on_rails_pro/spec/dummy/config/initializers/react_on_rails.rb
🚧 Files skipped from review as they are similar to previous changes (2)
- react_on_rails_pro/spec/dummy/app/views/layouts/application.html.erb
- react_on_rails_pro/spec/dummy/config/shakapacker.yml
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Learnt from: theforestvn88
Repo: shakacode/react_on_rails PR: 1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-10-08T20:53:47.076Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Learnt from: theforestvn88
Repo: shakacode/react_on_rails PR: 1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-07-27T10:08:35.868Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
Applied to files:
react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Applied to files:
react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: lint-js-and-ruby
- GitHub Check: claude-review
9c9abd1 to
ec22681
Compare
…ding strategy Apply the same defer loading strategy fix from commit d1a8a1a to the Pro dummy app to resolve race condition causing Playwright test timeouts. ## Problem Playwright E2E tests for streaming were timing out waiting for console message "ToggleContainer with title", indicating React components weren't hydrating. ## Root Cause The Pro dummy app was still using async: true for javascript_pack_tag while the open-source dummy app was updated to defer: true in commit d1a8a1a. This created a race condition where: - Generated component packs load asynchronously - Main client-bundle also loads asynchronously - If client-bundle executes before component registrations complete, React tries to hydrate unregistered components - ToggleContainer never hydrates, useEffect never runs, console.log never fires ## Solution 1. Changed javascript_pack_tag from async: true to defer: true in application.html.erb 2. Added precompile_hook to shakapacker.yml for pack generation 3. Added bin/shakapacker-precompile-hook script Using defer: true ensures script execution order - generated component packs load and register components before main bundle executes, preventing the race condition. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove unnecessary rubocop disable/enable directives - Fix Style/SymbolArray violation in shakapacker-precompile-hook - Add explanatory comment about generated_component_packs_loading_strategy defaulting to :defer to match OSS dummy app configuration Note: The failing "React Router Sixth Page" RSpec test is a known flaky test that also fails intermittently on master. This is not a regression from the defer loading strategy changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Revert Pro dummy app back to async: true (Pro supports Selective Hydration) - Fix ReScript build to run from Rails root instead of current directory - Use File.join for proper path resolution of config files - Wrap build commands in Dir.chdir(rails_root) for correct execution - Add early Rails root resolution with proper error handling - Remove unnecessary defer strategy comment from initializer - Add blank line before main execution section for style 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Convert precompile hooks from Ruby to bash and DRY them up: **Why Bash Over Ruby:** - Simpler: No Ruby stdlib dependencies - Faster: No Ruby interpreter startup overhead - Standard: Most build hooks are shell scripts - Cross-platform: Works everywhere (sh/bash universal) - Fewer dependencies: Doesn't require Ruby loaded **Changes:** - Create shared bash script in generator templates - Fix ReScript build to run from Rails root (cd into rails_root) - Fix pack generation to run from Rails root - Use proper path resolution with Rails root for all file checks - Replace Ruby scripts in both dummy apps with bash version - All three copies identical (generator template + 2 dummy apps) **Script Features:** - Finds Rails root by walking up directory tree - Detects ReScript config (bsconfig.json or rescript.json) - Runs ReScript builds from correct directory - Detects auto_load_bundle/components_subdirectory config - Generates packs when configured - Cross-platform package manager detection (yarn/npm) - Proper error handling and exit codes - Sets REACT_ON_RAILS_SKIP_VALIDATION for build context **Testing:** - Shellcheck passes with no warnings - Script executes successfully in non-pro dummy app - ReScript builds complete successfully - Pack generation runs successfully - All files have trailing newlines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
8ef957c to
0f91a20
Compare
Code ReviewSummaryThis PR converts the precompile hook from Ruby to POSIX shell script, which is a solid improvement for cross-platform compatibility and reduces dependencies. The implementation is clean and well-structured. However, there are several important issues that need to be addressed. Critical Issues1. Shell Script Execution Issue with
|
Consolidate duplicate precompile hook logic across OSS dummy app, Pro dummy app, and generator template into a single shared Ruby implementation. Changes: - Create lib/tasks/precompile/shakapacker_precompile_hook_shared.rb with common logic - Update spec/dummy/bin/shakapacker-precompile-hook to load shared implementation - Update react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook to load shared implementation - Simplify generator template to use Ruby instead of shell script (removes ReScript build logic) Benefits: - Eliminates 342 lines of duplicate code across 3 files - Easier to maintain and update precompile hook logic in one place - Consistent behavior across all environments - Better error handling with Ruby exceptions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Code Review - PR #1977SummaryThis PR refactors the Shakapacker precompile hook to add ReScript build support and consolidate duplicate code across dummy apps. The changes follow good DRY principles and improve the developer experience. ✅ Strengths1. Excellent Code ReuseThe creation of 2. Robust Error Handling
3. Security Improvements
4. Cross-Platform Compatibility
5. Good Documentation
🐛 Issues Found1. Critical: Regex Pattern Missing Comment ExclusionLocation: The regex patterns for detecting config need to exclude comments: # Current (INCORRECT - matches commented lines):
return unless initializer_content.match?(/^\s*config\.auto_load_bundle\s*=/) ||
initializer_content.match?(/^\s*config\.components_subdirectory\s*=/)
# Should be (matches only uncommented lines):
return unless initializer_content.match?(/^\s*(?\!#).*config\.auto_load_bundle\s*=/) ||
initializer_content.match?(/^\s*(?\!#).*config\.components_subdirectory\s*=/)This is inconsistent with the generator template version (lines 37-38), which correctly excludes comments. This could cause the hook to run even when auto-pack generation is commented out. 2. Platform Compatibility Issue:
|
…n available Update the generator template to intelligently use the shared precompile hook implementation when running in the gem's development/test context, while maintaining a self-contained fallback for generated user applications. This provides the best of both worlds: - Test dummy apps and generator template all use the same shared code - Generated user apps have a standalone copy that works independently - Easier to maintain and update the precompile hook logic The hook now tries to load the shared implementation from the gem first, and falls back to its inline implementation if the gem isn't available or accessible. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
|
/run-skipped-ci |
|
🚀 Full CI Mode Enabled ✅ All checks are already running - nothing to do! All CI checks are already running on this PR. Added the |
…mplate Address feedback to properly organize the precompile hook code: 1. Move shared implementation to spec/support/ (test-only location) - Renamed: lib/tasks/precompile/shakapacker_precompile_hook_shared.rb - To: spec/support/shakapacker_precompile_hook_shared.rb - This makes it clear the shared code is only for test dummy apps 2. Simplify generator template to be standalone - Remove logic to load shared implementation from gem - Generator template is for newly created production apps - Should be simple, self-contained, and production-ready - No references to development/testing concerns 3. Update test dummy apps to reference new location - spec/dummy/bin/shakapacker-precompile-hook - react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook - Both now load from spec/support/ The generator template is now clean and focused on production use cases, while test dummy apps share common implementation from the test support directory. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook(1 hunks)react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook(1 hunks)react_on_rails_pro/spec/dummy/config/shakapacker.yml(1 hunks)spec/dummy/bin/shakapacker-precompile-hook(1 hunks)spec/support/shakapacker_precompile_hook_shared.rb(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
spec/dummy/bin/shakapacker-precompile-hookspec/support/shakapacker_precompile_hook_shared.rblib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
spec/dummy/bin/shakapacker-precompile-hookspec/support/shakapacker_precompile_hook_shared.rblib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hookreact_on_rails_pro/spec/dummy/config/shakapacker.yml
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
Applied to files:
spec/dummy/bin/shakapacker-precompile-hookspec/support/shakapacker_precompile_hook_shared.rblib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
spec/dummy/bin/shakapacker-precompile-hookspec/support/shakapacker_precompile_hook_shared.rblib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Applied to files:
spec/support/shakapacker_precompile_hook_shared.rblib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
spec/support/shakapacker_precompile_hook_shared.rb
🧬 Code graph analysis (1)
spec/support/shakapacker_precompile_hook_shared.rb (2)
lib/react_on_rails/version_checker.rb (1)
package_json_path(223-225)lib/generators/react_on_rails/generator_helper.rb (1)
package_json(7-20)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: examples (3.4, latest)
- GitHub Check: examples (3.2, minimum)
- GitHub Check: rspec-package-tests (3.2, minimum)
- GitHub Check: rspec-package-tests (3.4, latest)
- GitHub Check: build-dummy-app-webpack-test-bundles (3.4, 22, latest)
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: build
- GitHub Check: pro-lint-js-and-ruby
- GitHub Check: claude-review
🔇 Additional comments (3)
lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook (1)
16-58: Pack-generation precompile hook logic looks solid.Rails root discovery, initializer scanning, and guarded
react_on_rails:generate_packsexecution with clear error handling all look correct and aligned with the shared implementation.react_on_rails_pro/spec/dummy/config/shakapacker.yml (1)
22-26: Precompile hook wiring and security guidance look good.The
precompile_hookis correctly configured underdefaultand the accompanying comments clearly call out security considerations around trusted script paths.spec/support/shakapacker_precompile_hook_shared.rb (1)
19-112: Shared ReScript + pack-generation hook is well-structured and reusable.The shared hook cleanly discovers Rails root, conditionally builds ReScript based on config/package.json, and generates React on Rails packs only when initializer settings are present, with appropriate error handling and a main guard that lets callers choose between direct execution and explicit invocation from wrapper scripts.
| # Find the gem root directory (three levels up from spec/dummy/bin) | ||
| gem_root = File.expand_path("../../..", __dir__) | ||
| shared_hook = File.join(gem_root, "spec", "support", "shakapacker_precompile_hook_shared.rb") | ||
|
|
||
| # Find Rails root by walking upward looking for config/environment.rb | ||
| def find_rails_root | ||
| dir = Dir.pwd | ||
| loop do | ||
| return dir if File.exist?(File.join(dir, "config", "environment.rb")) | ||
|
|
||
| parent = File.dirname(dir) | ||
| return nil if parent == dir # Reached filesystem root | ||
|
|
||
| dir = parent | ||
| end | ||
| end | ||
|
|
||
| # Build ReScript if needed | ||
| def build_rescript_if_needed | ||
| # Check for both old (bsconfig.json) and new (rescript.json) config files | ||
| return unless File.exist?("bsconfig.json") || File.exist?("rescript.json") | ||
|
|
||
| puts "🔧 Building ReScript..." | ||
|
|
||
| # Cross-platform package manager detection | ||
| yarn_available = system("yarn", "--version", out: File::NULL, err: File::NULL) | ||
| npm_available = system("npm", "--version", out: File::NULL, err: File::NULL) | ||
|
|
||
| success = if yarn_available | ||
| system("yarn", "build:rescript") | ||
| elsif npm_available | ||
| system("npm", "run", "build:rescript") | ||
| else | ||
| warn "⚠️ Warning: Neither yarn nor npm found. Skipping ReScript build." | ||
| return | ||
| end | ||
|
|
||
| if success | ||
| puts "✅ ReScript build completed successfully" | ||
| else | ||
| warn "❌ ReScript build failed" | ||
| exit 1 | ||
| end | ||
| end | ||
|
|
||
| # Generate React on Rails packs if needed | ||
| # rubocop:disable Metrics/CyclomaticComplexity | ||
| def generate_packs_if_needed | ||
| # Find Rails root directory | ||
| rails_root = find_rails_root | ||
| return unless rails_root | ||
|
|
||
| # Check if React on Rails initializer exists | ||
| initializer_path = File.join(rails_root, "config", "initializers", "react_on_rails.rb") | ||
| return unless File.exist?(initializer_path) | ||
|
|
||
| # Check if auto-pack generation is configured (match actual config assignments, not comments) | ||
| config_file = File.read(initializer_path) | ||
| # Match uncommented configuration lines only (lines not starting with #) | ||
| has_auto_load = config_file =~ /^\s*(?!#).*config\.auto_load_bundle\s*=/ | ||
| has_components_subdir = config_file =~ /^\s*(?!#).*config\.components_subdirectory\s*=/ | ||
| return unless has_auto_load || has_components_subdir | ||
|
|
||
| puts "📦 Generating React on Rails packs..." | ||
|
|
||
| # Cross-platform bundle availability check | ||
| bundle_available = system("bundle", "--version", out: File::NULL, err: File::NULL) | ||
| return unless bundle_available | ||
|
|
||
| # Check if rake task exists (use array form for security) | ||
| task_list = IO.popen(["bundle", "exec", "rails", "-T"], err: [:child, :out], &:read) | ||
| return unless task_list.include?("react_on_rails:generate_packs") | ||
|
|
||
| # Use array form for better cross-platform support | ||
| success = system("bundle", "exec", "rails", "react_on_rails:generate_packs") | ||
|
|
||
| if success | ||
| puts "✅ Pack generation completed successfully" | ||
| else | ||
| warn "❌ Pack generation failed" | ||
| exit 1 | ||
| end | ||
| end | ||
| # rubocop:enable Metrics/CyclomaticComplexity | ||
|
|
||
| # Main execution | ||
| begin | ||
| build_rescript_if_needed | ||
| generate_packs_if_needed | ||
|
|
||
| exit 0 | ||
| rescue StandardError => e | ||
| warn "❌ Precompile hook failed: #{e.message}" | ||
| warn e.backtrace.join("\n") | ||
| unless File.exist?(shared_hook) | ||
| warn "❌ Error: Shared precompile hook not found at #{shared_hook}" | ||
| exit 1 | ||
| end | ||
|
|
||
| # Load and execute the shared hook | ||
| load shared_hook |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Shared precompile hook is never executed (bin script only loads it).
This script currently just loads spec/support/shakapacker_precompile_hook_shared.rb and exits. Because the shared file only runs its main block when __FILE__ == $PROGRAM_NAME, loading it from this bin script does not trigger build_rescript_if_needed or generate_packs_if_needed, so the Shakapacker precompile hook becomes a no-op.
Explicitly invoke the shared methods after loading the file so ReScript builds and pack generation actually run.
Suggested fix:
gem_root = File.expand_path("../../..", __dir__)
shared_hook = File.join(gem_root, "spec", "support", "shakapacker_precompile_hook_shared.rb")
unless File.exist?(shared_hook)
warn "❌ Error: Shared precompile hook not found at #{shared_hook}"
exit 1
end
# Load and execute the shared hook
-load shared_hook
+load shared_hook
+
+# Run shared precompile steps
+build_rescript_if_needed
+generate_packs_if_neededI'll verify the review comment by inspecting the relevant files in the codebase.
🏁 Script executed:
#!/bin/bash
# Inspect the bin script
echo "=== BIN SCRIPT ==="
cat spec/dummy/bin/shakapacker-precompile-hook
echo -e "\n=== SHARED HOOK FILE ==="
cat spec/support/shakapacker_precompile_hook_shared.rbLength of output: 4345
Shared precompile hook is never executed (bin script only loads it).
This script currently just loads spec/support/shakapacker_precompile_hook_shared.rb and exits. Because the shared file only runs its main block when __FILE__ == $PROGRAM_NAME, loading it from this bin script does not trigger build_rescript_if_needed or generate_packs_if_needed, so the Shakapacker precompile hook becomes a no-op.
Explicitly invoke the shared methods after loading the file so ReScript builds and pack generation actually run.
Suggested fix:
gem_root = File.expand_path("../../..", __dir__)
shared_hook = File.join(gem_root, "spec", "support", "shakapacker_precompile_hook_shared.rb")
unless File.exist?(shared_hook)
warn "❌ Error: Shared precompile hook not found at #{shared_hook}"
exit 1
end
# Load and execute the shared hook
-load shared_hook
+load shared_hook
+
+# Run shared precompile steps
+build_rescript_if_needed
+generate_packs_if_needed📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Find the gem root directory (three levels up from spec/dummy/bin) | |
| gem_root = File.expand_path("../../..", __dir__) | |
| shared_hook = File.join(gem_root, "spec", "support", "shakapacker_precompile_hook_shared.rb") | |
| # Find Rails root by walking upward looking for config/environment.rb | |
| def find_rails_root | |
| dir = Dir.pwd | |
| loop do | |
| return dir if File.exist?(File.join(dir, "config", "environment.rb")) | |
| parent = File.dirname(dir) | |
| return nil if parent == dir # Reached filesystem root | |
| dir = parent | |
| end | |
| end | |
| # Build ReScript if needed | |
| def build_rescript_if_needed | |
| # Check for both old (bsconfig.json) and new (rescript.json) config files | |
| return unless File.exist?("bsconfig.json") || File.exist?("rescript.json") | |
| puts "🔧 Building ReScript..." | |
| # Cross-platform package manager detection | |
| yarn_available = system("yarn", "--version", out: File::NULL, err: File::NULL) | |
| npm_available = system("npm", "--version", out: File::NULL, err: File::NULL) | |
| success = if yarn_available | |
| system("yarn", "build:rescript") | |
| elsif npm_available | |
| system("npm", "run", "build:rescript") | |
| else | |
| warn "⚠️ Warning: Neither yarn nor npm found. Skipping ReScript build." | |
| return | |
| end | |
| if success | |
| puts "✅ ReScript build completed successfully" | |
| else | |
| warn "❌ ReScript build failed" | |
| exit 1 | |
| end | |
| end | |
| # Generate React on Rails packs if needed | |
| # rubocop:disable Metrics/CyclomaticComplexity | |
| def generate_packs_if_needed | |
| # Find Rails root directory | |
| rails_root = find_rails_root | |
| return unless rails_root | |
| # Check if React on Rails initializer exists | |
| initializer_path = File.join(rails_root, "config", "initializers", "react_on_rails.rb") | |
| return unless File.exist?(initializer_path) | |
| # Check if auto-pack generation is configured (match actual config assignments, not comments) | |
| config_file = File.read(initializer_path) | |
| # Match uncommented configuration lines only (lines not starting with #) | |
| has_auto_load = config_file =~ /^\s*(?!#).*config\.auto_load_bundle\s*=/ | |
| has_components_subdir = config_file =~ /^\s*(?!#).*config\.components_subdirectory\s*=/ | |
| return unless has_auto_load || has_components_subdir | |
| puts "📦 Generating React on Rails packs..." | |
| # Cross-platform bundle availability check | |
| bundle_available = system("bundle", "--version", out: File::NULL, err: File::NULL) | |
| return unless bundle_available | |
| # Check if rake task exists (use array form for security) | |
| task_list = IO.popen(["bundle", "exec", "rails", "-T"], err: [:child, :out], &:read) | |
| return unless task_list.include?("react_on_rails:generate_packs") | |
| # Use array form for better cross-platform support | |
| success = system("bundle", "exec", "rails", "react_on_rails:generate_packs") | |
| if success | |
| puts "✅ Pack generation completed successfully" | |
| else | |
| warn "❌ Pack generation failed" | |
| exit 1 | |
| end | |
| end | |
| # rubocop:enable Metrics/CyclomaticComplexity | |
| # Main execution | |
| begin | |
| build_rescript_if_needed | |
| generate_packs_if_needed | |
| exit 0 | |
| rescue StandardError => e | |
| warn "❌ Precompile hook failed: #{e.message}" | |
| warn e.backtrace.join("\n") | |
| unless File.exist?(shared_hook) | |
| warn "❌ Error: Shared precompile hook not found at #{shared_hook}" | |
| exit 1 | |
| end | |
| # Load and execute the shared hook | |
| load shared_hook | |
| # Find the gem root directory (three levels up from spec/dummy/bin) | |
| gem_root = File.expand_path("../../..", __dir__) | |
| shared_hook = File.join(gem_root, "spec", "support", "shakapacker_precompile_hook_shared.rb") | |
| unless File.exist?(shared_hook) | |
| warn "❌ Error: Shared precompile hook not found at #{shared_hook}" | |
| exit 1 | |
| end | |
| # Load and execute the shared hook | |
| load shared_hook | |
| # Run shared precompile steps | |
| build_rescript_if_needed | |
| generate_packs_if_needed |
🤖 Prompt for AI Agents
In spec/dummy/bin/shakapacker-precompile-hook around lines 9 to 19, the script
only loads spec/support/shakapacker_precompile_hook_shared.rb so its main block
(guarded by __FILE__ == $PROGRAM_NAME) never runs; after loading the shared file
explicitly call the methods that perform the work (e.g. build_rescript_if_needed
and generate_packs_if_needed), check their return values or rescue exceptions to
surface failures, log or warn on error and exit with non‑zero status when those
calls fail so the precompile hook actually performs ReScript builds and pack
generation.
Replace overly complicated 58-line Ruby implementation with a simple 22-line bash script that just checks for auto_load_bundle configuration and runs the rake task if needed. The bash script is: - Easier to read and understand - Fewer dependencies (no Ruby parsing, no fileutils) - Standard shell script that works everywhere - Does exactly what's needed, nothing more Perfect for newly generated production apps. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Key improvements to shakapacker precompile hook: 1. **Better error messages for ReScript builds** - Fail fast when ReScript config exists but package.json is missing - Fail fast when build:rescript script is not defined - Provide actionable error messages with exact fixes needed - Add JSON parse error handling 2. **More robust config detection** - Improved regex to properly ignore commented configuration lines - Allow flexible spacing in config assignments - Prevent false positives from commented-out config 3. **Consistent error handling** - Changed warnings to errors when builds are required but missing - Exit with status 1 on all failure scenarios - Better distinction between optional vs required failures Addresses code review feedback on error handling and regex robustness. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook(1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
Applied to files:
lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: build-dummy-app-webpack-test-bundles (3.4, 22, latest)
- GitHub Check: rspec-package-tests (3.4, latest)
- GitHub Check: rspec-package-tests (3.2, minimum)
- GitHub Check: examples (3.2, minimum)
- GitHub Check: examples (3.4, latest)
- GitHub Check: pro-lint-js-and-ruby
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: build
- GitHub Check: claude-review
🔇 Additional comments (2)
lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook (2)
1-12: LGTM! Clean setup and documentation.The POSIX shell shebang, error handling via
set -e, and validation skip are appropriate for a precompile hook.
1-22: Inconsistency with PR objectives regarding ReScript support.The PR objectives describe "a cross-platform Ruby script" that "detects ReScript config files (bsconfig.json and rescript.json) and runs ReScript build," but this implementation is a shell script without ReScript detection or build support.
Please clarify: Is ReScript support intended for this generator template, or is it only implemented in the spec/dummy shared hook? The current implementation differs significantly from the described functionality.
lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook
Outdated
Show resolved
Hide resolved
| if [ -f "config/initializers/react_on_rails.rb" ]; then | ||
| if grep -q "config\.auto_load_bundle" config/initializers/react_on_rails.rb || \ | ||
| grep -q "config\.components_subdirectory" config/initializers/react_on_rails.rb; then | ||
| echo "📦 Generating React on Rails packs..." | ||
| bundle exec rails react_on_rails:generate_packs | ||
| echo "✅ Pack generation completed successfully" | ||
| fi | ||
| fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
grep patterns match commented-out configuration lines.
The grep patterns will incorrectly match commented lines like # config.auto_load_bundle = true, triggering unnecessary pack generation. Additionally, there's no verification that the react_on_rails:generate_packs rake task exists before attempting to run it.
Apply this diff to exclude commented lines and verify the task exists:
# Generate packs if React on Rails is configured with auto_load_bundle
if [ -f "config/initializers/react_on_rails.rb" ]; then
- if grep -q "config\.auto_load_bundle" config/initializers/react_on_rails.rb || \
- grep -q "config\.components_subdirectory" config/initializers/react_on_rails.rb; then
+ if grep -v '^\s*#' config/initializers/react_on_rails.rb | grep -q "config\.auto_load_bundle" || \
+ grep -v '^\s*#' config/initializers/react_on_rails.rb | grep -q "config\.components_subdirectory"; then
+ # Verify the rake task exists before running it
+ if ! bundle exec rails -T | grep -q "react_on_rails:generate_packs"; then
+ echo "⚠️ react_on_rails:generate_packs task not found, skipping pack generation"
+ exit 0
+ fi
echo "📦 Generating React on Rails packs..."
bundle exec rails react_on_rails:generate_packs
echo "✅ Pack generation completed successfully"Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook
around lines 15 to 22, the current grep matches commented configuration lines
and runs the rake task unconditionally; update the checks to only match
non-commented lines (e.g. use a pattern that requires optional leading
whitespace then a non-# character before the config key) and add a pre-check
that the react_on_rails:generate_packs task exists (e.g. use bundle exec rails
-T or rake -T and grep for the exact task name) before invoking bundle exec
rails react_on_rails:generate_packs, running the generation only if both a
non-commented config line is present and the task is available.
…late The SKIP_VALIDATION environment variable is not needed in the generator template because: 1. By the time apps use this hook, packages are already installed 2. The engine's skip_version_validation? already handles edge cases: - package_json_missing? check - running_generator? check 3. This was cargo-culted from the Ruby version which needed it for different reasons (loading Rails environment directly) The shared implementation in spec/support still sets this variable because test apps have different requirements, but user-generated apps should rely on the engine's built-in validation logic. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Code Review: Shakapacker Precompile Hook ImplementationThank you for this well-structured PR! The implementation shows good evolution through the commit history. Here's my detailed review: ✅ Strengths
🔍 Issues & Suggestions1. Regex Pattern Robustness (Critical)Location: The current regex pattern has a subtle bug: # Current (problematic)
return unless initializer_content.match?(/^\s*(?\!#).*config\.auto_load_bundle\s*=/) ||
initializer_content.match?(/^\s*(?\!#).*config\.components_subdirectory\s*=/)Problem: The negative lookahead # config.auto_load_bundle = true # ← This WILL match (false positive)
x = 1 # config.auto_load_bundle = true # ← This WILL match (false positive)Solution: Use a proper pattern that ensures the line doesn't start with # Fixed version
return unless initializer_content.match?(/^\s*[^#\s].*config\.auto_load_bundle\s*=/) ||
initializer_content.match?(/^\s*[^#\s].*config\.components_subdirectory\s*=/)This pattern:
Same issue exists in the bash template ( # Current (has same issue)
if grep -E "^[[:space:]]*[^#]*config\.auto_load_bundle[[:space:]]*=" ...
# Fixed version
if grep -E "^[[:space:]]*[^#[:space:]].*config\.auto_load_bundle[[:space:]]*=" ...2. Rails Root Discovery Logic (Medium Priority)Location: def find_rails_root
dir = Dir.pwd
while dir \!= "/"
return dir if File.exist?(File.join(dir, "config", "environment.rb"))
dir = File.dirname(dir)
end
nil
endIssues:
Solution: def find_rails_root
dir = Dir.pwd
loop do
return dir if File.exist?(File.join(dir, "config", "environment.rb"))
parent = File.dirname(dir)
return nil if parent == dir # Reached filesystem root (works cross-platform)
dir = parent
end
endThis pattern:
3. Package Manager Detection (Low Priority)Location: if system("which yarn > /dev/null 2>&1")Issues:
Better Approach: # More robust package manager detection
yarn_available = system("yarn", "--version", out: File::NULL, err: File::NULL)
npm_available = system("npm", "--version", out: File::NULL, err: File::NULL)
if yarn_available
system("yarn", "build:rescript", exception: true)
elsif npm_available
system("npm", "run", "build:rescript", exception: true)
else
warn "❌ Error: Neither yarn nor npm found but ReScript build required"
warn " Install yarn or npm to build ReScript files"
exit 1
endThis is:
4. Error Message Consistency (Low Priority)The error handling is inconsistent between ReScript builds and pack generation: ReScript build: Exits with status 1 on failure (correct)
Issue: The Suggestion: Either:
5. Missing Test Coverage ConsiderationThe PR test plan shows:
Recommendation: Before merging, verify:
Consider adding a simple test that validates the shared hook logic, especially the regex matching. 🔒 Security Assessment✅ No security concerns identified
🚀 Performance Considerations✅ Efficient implementation
📝 Documentation & Code StyleStrengths:
Minor Suggestion: #\!/bin/sh
# Simple bash implementation for production apps (no Ruby startup overhead)
# Shakapacker precompile hook for React on Rails🎯 SummaryThis is a well-architected PR that thoughtfully solves the precompile hook problem. The main issues are:
Recommendation: Fix the regex pattern (critical issue) before merging. The Windows compatibility issue is lower priority unless Windows support is a goal. The code quality is high, and the evolution through commits shows great attention to feedback and iterative improvement. Excellent work! 🎉 Files Reviewed:
|
Reverted from the hacky bash grep approach back to a clean Ruby script. The bash version had several problems: 1. Hacky grep parsing to detect config - fragile and error-prone 2. Duplicates logic that already exists in PacksGenerator 3. More complex (20+ lines vs 21 lines) 4. Harder to maintain The Ruby version is superior because: 1. Leverages PacksGenerator.generate_packs_if_stale which already knows when to generate vs skip 2. Simple and clean - just load Rails and call the method 3. Proper error handling with backtrace 4. Let the engine's built-in validation logic handle skipping The engine already skips validation appropriately via: - package_json_missing? check - running_generator? check So we don't need REACT_ON_RAILS_SKIP_VALIDATION here either. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Code Review for PR #1977Thank you for this well-structured PR! The precompile hook implementation is solid and follows good practices. Here's my detailed review: ✅ Strengths
🔍 Potential Issues & Suggestions1. Race Condition in
|
Restored the original implementation which had: 1. Rainbow colored output for better UX during builds 2. ENV["REACT_ON_RAILS_SKIP_VALIDATION"] = "true" because this hook loads Rails environment and validation would fail during builds The SKIP_VALIDATION is needed here because: - The hook loads Rails via require_relative "../config/environment" - This triggers the engine's validation initializer - During precompile, packages may not be fully available yet - Setting this env var prevents spurious validation failures 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…se-otp-timing * origin/master: (27 commits) Fix doctor command false version mismatch for beta/prerelease versions (#2064) Fix beta/RC version handling in generator (#2066) Document Rails Engine development nuances and add tests for automatic rake task loading (#2067) Add /run-skipped-tests as alias for /run-skipped-ci (#XXXX) (#2068) Fix: Add Rails 5.2-6.0 compatibility for compact_blank (#2058) Break CI circular dependency with non-docs change (#2065) Fix CI safety check to evaluate latest workflow attempt (#2062) Fix yalc publish (#2054) Add Shakapacker 9.0+ private_output_path integration for server bundles (#2028) Consolidate all beta versions into v16.2.0.beta.10 (#2057) Improve reliability of CI debugging scripts (#2056) Clarify monorepo changelog structure in documentation (#2055) Bump version to 16.2.0.beta.10 Bump version to 16.2.0.beta.9 Fix duplicate rake task execution by removing explicit task loading (#2052) Simplify precompile hook and restore Pro dummy app to async loading (#2053) Add Shakapacker precompile hook with ReScript support to Pro dummy app (#1977) Guard master docs-only pushes and ensure full CI runs (#2042) Refactor: Extract JS dependency management into shared module (#2051) Add workflow to detect invalid CI command attempts (#2037) ... # Conflicts: # rakelib/release.rake
### Summary Cleaned up the v16.2.0.beta.11 changelog by removing entries that are not relevant to end users upgrading from 16.1.x. After thorough investigation of each PR, removed developer-only tooling fixes and fixes for bugs introduced during the beta cycle. ### Changes Made **Removed developer-only entries:** - PR 2054: Yalc publish path resolution (yalc is developer tooling for local package testing) - PR 1873: Git dependency support (developer workflow feature) **Removed beta-specific entries:** - PR 2066: Generator version handling for beta/RC releases (only affects beta installations, not stable) - PR 2070: bin/dev NameError fix (bug introduced by PR #1977 during beta cycle) - PR 2023: bin/dev --verbose option fix (bin/dev is a new feature added in beta) - PR 2021: Shakapacker template configuration (fix specific to new bin/dev feature) **Kept legitimate user-facing fixes:** - PR 2064: Doctor command version mismatch detection (affects all users, not just beta) - PR 2052: Duplicate rake task execution (bug existed since 16.0.0) - PR 2058: Rails 5.2-6.0 compatibility (bug existed in 16.1.1) ### Pull Request checklist - [x] ~Add/update test to cover these changes~ - [x] ~Update documentation~ - [x] Update CHANGELOG file ### Other Information Each entry was carefully investigated by checking git history to determine when bugs were introduced (before or after 16.1.x) and whether features/fixes are relevant to end users versus gem developers. --------- Co-authored-by: Claude <noreply@anthropic.com>
Summary
Adds a Shakapacker precompile hook to the Pro dummy app to support automatic ReScript builds and React on Rails pack generation before webpack compilation.
Changes
1. Added Precompile Hook Script (
bin/shakapacker-precompile-hook)bsconfig.json(legacy) andrescript.jsonconfig filesreact_on_rails:generate_packswhenauto_load_bundleorcomponents_subdirectoryare configured2. Configured Shakapacker (
config/shakapacker.yml)precompile_hook: 'bin/shakapacker-precompile-hook'settingTechnical Details
ReScript Build Logic
File.join(rails_root, ...)for proper path resolutionDir.chdir(rails_root)package.jsonPack Generation Logic
auto_load_bundleorcomponents_subdirectorysettings (ignores comments)react_on_rails:generate_packsrake task existsWhy This Matters
The precompile hook ensures that:
Test Plan
bin/shakapackercompilationRelated
🤖 Generated with Claude Code
This change is
Summary by CodeRabbit
New Features
Chores